﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Collections.Generic;

using JavaSubstitutes;
using Sonar;

namespace Sprites
{
	public class Art
	{
		public static readonly int SAMPLE_BREAK_BLOCK = 0;
		public static readonly int SAMPLE_GET_COIN = 1;
		public static readonly int SAMPLE_MARIO_JUMP = 2;
		public static readonly int SAMPLE_MARIO_STOMP = 3;
		public static readonly int SAMPLE_MARIO_KICK = 4;
		public static readonly int SAMPLE_MARIO_POWER_UP = 5;
		public static readonly int SAMPLE_MARIO_POWER_DOWN = 6;
		public static readonly int SAMPLE_MARIO_DEATH = 7;
		public static readonly int SAMPLE_ITEM_SPROUT = 8;
		public static readonly int SAMPLE_CANNON_FIRE = 9;
		public static readonly int SAMPLE_SHELL_BUMP = 10;
		public static readonly int SAMPLE_LEVEL_EXIT = 11;
		public static readonly int SAMPLE_MARIO_1UP = 12;
		public static readonly int SAMPLE_MARIO_FIREBALL = 13;

		public static Image[,] mario;
		public static Image[,] smallMario;
		public static Image[,] fireMario;
		public static Image[,] enemies;
		public static Image[,] items;
		public static Image[,] level;
		public static Image[,] particles;
		public static Image[,] font;
		public static Image[,] bg;
		public static Image[,] map;
		public static Image[,] endScene;
		public static Image[,] gameOver;
		public static Image logo;
		public static Image titleScreen;

		public static SonarSample[] samples = new SonarSample[100]; //TODO: get the actual number

		public static SonarSample[] songs = new SonarSample[10];

		//private static Sequence[] songs = new Sequence[10];
		//private static Sequencer sequencer; //consider: why's all this stuff static? Private static?

		protected static bool HasBeenInitialized = false; //todo: let's refactor this to simply make this part of the constructor
		//protected static List<Image> ImagesLeftToLoad = new List<Image>();
		protected static int ImagesLeftToLoad = -1;

		public static bool FinishedInitializing
		{
			get { return ImagesLeftToLoad == 0; }
		}

		public static void init(GraphicsConfiguration gc, SonarSoundEngine sound)
		{
			if (HasBeenInitialized) return;
			HasBeenInitialized = true;

			//try
			//{
			//mario = cutImage(gc, layoutRoot, "/Assets/Images/mariosheet.png", 32, 32, 480, 32);
			//smallMario = cutImage(gc, layoutRoot, "/Assets/Images/smallmariosheet.png", 16, 16, 176, 16);
			//fireMario = cutImage(gc, layoutRoot, "/Assets/Images/firemariosheet.png", 32, 32, 480, 32);
			//enemies = cutImage(gc, layoutRoot, "/Assets/Images/enemysheet.png", 16, 32, 256, 256);
			//items = cutImage(gc, layoutRoot, "/Assets/Images/itemsheet.png", 16, 16, 256, 256);
			//level = cutImage(gc, layoutRoot, "/Assets/Images/mapsheet.png", 16, 16, 256, 256);
			//map = cutImage(gc, layoutRoot, "/Assets/Images/worldmap.png", 16, 16, 256, 256);
			//particles = cutImage(gc, layoutRoot, "/Assets/Images/particlesheet.png", 8, 8, 64, 64);
			//bg = cutImage(gc, layoutRoot, "/Assets/Images/bgsheet.png", 32, 32, 256, 320);
			//logo = getImage(gc, "/logo.png");
			//titleScreen = getImage(gc, "/title.png");
			//font = cutImage(gc, layoutRoot, "/Assets/Images/font.png", 8, 8, 768, 64);
			//endScene = cutImage(gc, layoutRoot, "/Assets/Images/endscene.png", 96, 96, 192, 96);
			//gameOver = cutImage(gc, layoutRoot, "/Assets/Images/gameovergost.png", 96, 64, 576, 64);

			//This is ugly! But basically- we have to know -ahead of time- how many total images we're loading in order to 
			//know if we've loaded them all. We can't calculate this on the fly when LoadPrecutImages because it's possible
			//to load all of the images before the next call to LoadPrecutImages, and then it would LOOK like we'd loaded them all
			//for a bit before the next call and if you checked to see if we were finished during that time, it'd be true, and things
			//would fail if you started off the rest of the code because we weren't actually done
			ImagesLeftToLoad =
				(480 / 32) * (32 / 32) +
				(176 / 16) * (16 / 16) +
				(480 / 32) * (32 / 32) +
				(256 / 16) * (256 / 32) + //enemies
				(256 / 16) * (256 / 16) + //itemSheet
				(256 / 16) * (256 / 16) +//mapSheet
				(256 / 16) * (256 / 16) +//worldMap
				(64 / 8) * (64 / 8) + //particleSheet
				(256 / 32) * (320 / 32) + //1 + 1 + //bhSheet + logo + title
				(768 / 8) * (64 / 8) + //font
				(192 / 96) * (96 / 96) + //endScene
				(576 / 96) * (64 / 64); //gameOver
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/mariosheet", ".png", ref mario, 32, 32, 480, 32);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/smallmariosheet", ".png", ref smallMario, 16, 16, 176, 16);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/firemariosheet", ".png", ref fireMario, 32, 32, 480, 32);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/enemysheet", ".png", ref enemies, 16, 32, 256, 256);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/itemsheet", ".png", ref items, 16, 16, 256, 256);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/mapsheet", ".png", ref level, 16, 16, 256, 256);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/worldmap", ".png", ref map, 16, 16, 256, 256);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/particlesheet", ".png", ref particles, 8, 8, 64, 64);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/bgsheet", ".png", ref bg, 32, 32, 256, 320); //8 tiles destX 10
			getImage(gc, "/infiniteMarioDotNet;component/Assets/Images/logo.png", ref logo);
			getImage(gc, "/infiniteMarioDotNet;component/Assets/Images/title.png", ref titleScreen);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/font", ".png", ref font, 8, 8, 768, 64);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/endscene", ".png", ref endScene, 96, 96, 192, 96);
			LoadPrecutImages(gc, "/infiniteMarioDotNet;component/Assets/Images/gameovergost", ".png", ref gameOver, 96, 64, 576, 64);

			//TODO: why's this in -art.cs- ?
			samples[SAMPLE_BREAK_BLOCK] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/breakblock.wma");
			samples[SAMPLE_GET_COIN] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/coin.wma");
			samples[SAMPLE_MARIO_JUMP] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/jump.wma");
			samples[SAMPLE_MARIO_STOMP] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/stomp.wma");
			samples[SAMPLE_MARIO_KICK] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/kick.wma");
			samples[SAMPLE_MARIO_POWER_UP] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/powerup.wma");
			samples[SAMPLE_MARIO_POWER_DOWN] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/powerdown.wma");
			samples[SAMPLE_MARIO_DEATH] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/death.wma");
			samples[SAMPLE_ITEM_SPROUT] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/sprout.wma");
			samples[SAMPLE_CANNON_FIRE] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/cannon.wma");
			samples[SAMPLE_SHELL_BUMP] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/bump.wma");
			samples[SAMPLE_LEVEL_EXIT] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/exit.wma");
			samples[SAMPLE_MARIO_1UP] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/1-up.wma");
			samples[SAMPLE_MARIO_FIREBALL] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/sound/fireball.wma");
			//}

			//sequencer = MidiSystem.getSequencer();
			//if (sequencer != null)
			//{
			//    sequencer.open();
			songs[0] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/music/smb3map1.wma", true);
			songs[1] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/music/smwovr1.wma", true);
			songs[2] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/music/smb3undr.wma", true);
			songs[3] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/music/smwfortress.wma", true);
			songs[4] = sound.loadSample(gc, "/infiniteMarioDotNet;component/Assets/music/smwtitle.wma", true);
		}

		private static void getImage(GraphicsConfiguration gc, String imageName, ref Image img)
		{
			img = new Image();
			img.ImageOpened += (sender, args) => ImagesLeftToLoad--;
			img.ImageFailed += (sender, args) => { throw new Exception("Couldn't load: " + imageName); };
			img.Source = new BitmapImage(new Uri(imageName, UriKind.Relative)) { CreateOptions = BitmapCreateOptions.None };

			gc.LayoutRoot.Children.Add(img);
		}


		public static void LoadPrecutImages(GraphicsConfiguration gc, String baseImageName, string extension, ref Image[,] images, int xSize, int ySize, int totalWidth, int totalHeight)
		{
			images = new Image[totalWidth / xSize, totalHeight / ySize];

			int i = 0;
			for (int y = 0; y < totalHeight / ySize; y++)
				for (int x = 0; x < totalWidth / xSize; x++)
				{
					Uri uriToLoad = new Uri(baseImageName + i.ToString("000") + extension, UriKind.Relative);
					images[x, y] = new Image() { Source = new BitmapImage(uriToLoad) };
					//ImagesLeftToLoad--;
					images[x, y].ImageOpened += (sender, args) => { Art_ImageOpened(sender, args); };
					images[x, y].ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(Art_ImageFailed);
					Canvas.SetZIndex(images[x, y], 2);
					Canvas.SetLeft(images[x, y], 0);
					Canvas.SetTop(images[x, y], 0);
					gc.LayoutRoot.Children.Add(images[x, y]);
					i++;
				}
		}

		static void Art_ImageOpened(object sender, RoutedEventArgs e)
		{
			ImagesLeftToLoad--; //does this need to be locked?
			//Debug.WriteLine("LoadedPrecut: " + ((BitmapImage)((Image)sender).Source).UriSource.ToString());
		}

		private static void Art_ImageFailed(object sender, ExceptionRoutedEventArgs e)
		{
			throw new Exception("Couldn't load image: " + ((BitmapImage)((Image)sender).Source).UriSource.ToString());
		}

		private static void LoadAndCutImage(GraphicsConfiguration gc, String imageName, Image[,] cutImages, int xSize, int ySize, int totalWidth, int totalHeight)
		{
			JavaSubstitutes.WriteableBitmapExtensions.LoadWriteableBitmap(new Uri(imageName, UriKind.Relative), xSize, ySize, gc.LayoutRoot, cutImages,
				null,
				(sender, args) => { throw new Exception("Couldn't load image:" + imageName); },
				(state, bmp) =>
				{
					cutImage(gc, imageName, bmp, cutImages, xSize, ySize, totalWidth, totalHeight);
				}
				);
		}

		//there's no good way to load the image ahead of putting it onto a page, so we can pass in the dimenions since they're already known
		//private static Image[,] cutImage(GraphicsConfiguration gc, Canvas layoutRoot, String imageName, WriteableBitmap bmpToCut, Image[,] cutImages, int xSize, int ySize, int totalWidth, int totalHeight)
		private static void cutImage(GraphicsConfiguration gc, String imageName, WriteableBitmap bmpToCut, Image[,] cutImages, int xSize, int ySize, int totalWidth, int totalHeight)
		{
			//meliot: looks like this divides it into tiles
			//getImage(gc, imageName, (sender, RoutedEventArgs) =>
			//{
			cutImages = new Image[totalWidth / xSize, totalHeight / ySize];

			for (int x = 0; x < totalWidth / xSize; x++)
			{
				for (int y = 0; y < totalHeight / ySize; y++)
				{
					cutImages[x, y] = new Image() { Source = bmpToCut.Crop(x * xSize, y * ySize, xSize, ySize) };
				}
			}

			//gc.LayoutRoot.Children.Remove((Image)sender);
			//gc.LayoutRoot.Children.Remove((Image)sender);
			//});
		}




		////there's no good way to load the image ahead of putting it onto a page, so we can pass in the dimenions since they're already known
		//private static Image[,] cutImage(GraphicsConfiguration gc,Canvas layoutRoot, String imageName, int xSize, int ySize, int totalWidth, int totalHeight)
		//{
		//    //meliot: looks like this divides it into tiles
		//    Image source = getImage(gc, imageName);

		//    Image[,] images = new Image[totalWidth / xSize, totalHeight / ySize];

		//    for (int destX = 0; destX < totalWidth / xSize; destX++)
		//    {
		//        for (int destY = 0; destY < totalHeight / ySize; destY++)
		//        {
		//            //take1
		//            //BitmapImage test =(BitmapImage) source.Source;
		//            //WriteableBitmap wbmp = new WriteableBitmap((BitmapImage)(source.Source));

		//            //take2
		//            WriteableBitmap wbmp = JavaSubstitutes.WriteableBitmapExtensions.LoadWriteableBitmap(new Uri(imageName, UriKind.Relative), layoutRoot, 
		//                null,
		//                (sender, args) => { throw new Exception("Couldn't load image:" + imageName); });

		//            images[destX, destY] = new Image() { Source = wbmp.Crop(destX * xSize, destY * ySize, xSize, ySize) };
		//        }
		//    }

		//    return images;
		//}

		//todo: move this OUT of art!
		public static void startMusic(int index)
		{
			foreach (SonarSample song in songs)
			{
				if (song == null) continue;
				song.Sound.MediaEnded -= SonarSoundEngine.Sound_MediaEnded;
				song.Sound.Stop();				
			}

			songs[index].Sound.Position = new TimeSpan();
			songs[index].Sound.Play();
			

			//songs[index].Sound.CurrentStateChanged += new RoutedEventHandler(Sound_CurrentStateChanged);
			//meliot: loopinbg?




			//return; 
			//stopMusic();
			//if (sequencer != null)
			//{
			//    try
			//    {
			//        sequencer.open();
			//        sequencer.setSequence((Sequence)null);
			//        sequencer.setSequence(songs[song]);
			//        sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
			//        sequencer.start();
			//    }
			//    catch (Exception e)
			//    {
			//    }
			//}
		}

		static void Sound_CurrentStateChanged(object sender, RoutedEventArgs e)
		{

		}

		public static void stopMusic()
		{
			foreach (SonarSample song in songs)
			{
				if (song == null) continue;
				song.Sound.Stop();
			}
			//return; //TODO: implement sounds
			//if (sequencer != null)
			//{
			//    try
			//    {
			//        sequencer.stop();
			//        sequencer.close();
			//    }
			//    catch (Exception e)
			//    {
			//    }
			//}
		}
	}
}

